import threading
import time
import serial
import serial.tools.list_ports
from flask import Flask, render_template, request, jsonify
from flask import Blueprint


us = Blueprint('usart', __name__)

# 全局变量
serial_port = None  # 串口信息
baud_rate = 9600
port_name = None
running = True
received_data = []
max_received_lines = 100
heartbeat_enabled = True
heartbeat_interval = 5  # 默认5秒

# 线程锁
thread_lock = threading.Lock()


# """查找可用的串口"""
def find_serial_ports():
    """查找可用的串口"""
    ports = serial.tools.list_ports.comports()
    available_ports = []

    for port in ports:
        available_ports.append({
            'device': port.device,
            'description': port.description
        })

    return available_ports


# """初始化串口"""
def serial_init(port, baud=9600):
    """初始化串口"""
    global serial_port, port_name, baud_rate

    # 先关闭现有连接
    if serial_port and serial_port.is_open:
        serial_port.close()

    port_name = port
    baud_rate = baud

    try:
        serial_port = serial.Serial(
            port=port_name,
            baudrate=baud_rate,
            timeout=1
        )
        print(f"串口 {port_name} 初始化成功")
        return True
    except Exception as e:
        print(f"串口初始化失败: {e}")
        return False


# """发送数据到串口"""
def serial_send(data, format_type='text'):
    """发送数据到串口"""
    global serial_port

    if serial_port and serial_port.is_open:
        try:
            if format_type == 'hex':
                # 16进制模式：将16进制字符串转换为字节
                bytes_data = bytes.fromhex(data)
                serial_port.write(bytes_data)
                print(f"发送(HEX): {data}")
            else:
                # 文本模式
                serial_port.write(data.encode('utf-8'))
                print(f"发送(TEXT): {data.strip()}")

            return True
        except Exception as e:
            print(f"发送错误: {e}")
            return False
    else:
        print("串口未打开")
        return False


# """接收线程函数"""
def receive_thread_function():
    """接收线程函数"""
    global serial_port, running, received_data

    print('接收线程 T1 开始')

    while running:
        if serial_port and serial_port.is_open:
            try:
                if serial_port.in_waiting > 0:
                    # 读取原始字节数据
                    raw_data = serial_port.read(serial_port.in_waiting)
                    if raw_data:
                        with thread_lock:
                            # 尝试UTF-8解码
                            try:
                                text_data = raw_data.decode('utf-8', errors='ignore')
                                if text_data.strip():
                                    lines = text_data.strip().split('\n')
                                    for line in lines:
                                        if line.strip():
                                            received_data.append({
                                                'type': 'received',
                                                'data': line.strip(),
                                                'time': time.strftime('%H:%M:%S'),
                                                'format': 'text'
                                            })
                            except:
                                pass

                            # 同时显示16进制格式
                            hex_data = raw_data.hex().upper()
                            # 每2个字符一组，用空格分隔
                            formatted_hex = ' '.join([hex_data[i:i + 2] for i in range(0, len(hex_data), 2)])
                            if formatted_hex:
                                received_data.append({
                                    'type': 'received',
                                    'data': formatted_hex,
                                    'time': time.strftime('%H:%M:%S'),
                                    'format': 'hex'
                                })

                            # 保持最多max_received_lines行数据
                            if len(received_data) > max_received_lines:
                                received_data = received_data[-max_received_lines:]
            except Exception as e:
                print(f"接收错误: {e}")

        time.sleep(0.1)  # 每100ms检查一次

    print('接收线程 T1 结束')

    """计算CRC8校验值"""


# """计算CRC8校验值"""
def calculate_crc8(data):
    """计算CRC8校验值"""
    # 使用CRC-8算法（多项式0x07），初始值0x00
    crc = 0x00
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ 0x07
            else:
                crc <<= 1
            crc &= 0xFF
    return crc


# """心跳线程函数"""
def heartbeat_thread_function():
    """心跳线程函数"""
    global serial_port, running, heartbeat_enabled, heartbeat_interval

    print('心跳线程 T2 开始')
    heartbeat_count = 0

    while running:
        heartbeat_count += 1

        # 根据配置的间隔发送心跳包
        interval_ticks = heartbeat_interval * 10  # 转换为0.1秒的倍数
        if heartbeat_count % interval_ticks == 0:  # 根据配置间隔发送
            if heartbeat_enabled and serial_port and serial_port.is_open:
                # 心跳包格式：帧头(55) + 帧ID(01) + 帧长度(01) + 1Byte数据(01) + CRC8校验
                heartbeat_data = [0x55, 0x01, 0x01, 0x01]  # 帧头、帧ID、帧长度、数据

                # 计算CRC8校验（校验区间0~3，即整个数据包）
                crc = calculate_crc8(heartbeat_data)
                heartbeat_data.append(crc)

                # 转换为16进制字符串
                hex_string = ''.join([f'{b:02X}' for b in heartbeat_data])

                # 发送16进制心跳包
                serial_send(hex_string, 'hex')

                # 在接收数据中显示心跳包发送记录
                with thread_lock:
                    received_data.append({
                        'type': 'sent',
                        'data': hex_string,
                        'time': time.strftime('%H:%M:%S'),
                        'format': 'hex'
                    })

        time.sleep(0.1)  # 每100ms检查一次

    print('心跳线程 T2 结束')


# 线程对象
receive_thread = None
heartbeat_thread = None


# """启动串口通信线程"""
def start_serial_threads():
    """启动串口通信线程"""
    global receive_thread, heartbeat_thread, running

    running = True

    # 创建线程对象
    receive_thread = threading.Thread(target=receive_thread_function, name='T1_Receive')
    #heartbeat_thread = threading.Thread(target=heartbeat_thread_function, name='T2_Heartbeat')#暂时取消心跳任务

    # 启动线程
    receive_thread.start()
    # heartbeat_thread.start()  # 暂时取消心跳任务

    print(f'当前存活的线程数量: {threading.active_count()}')
    print(f'当前所有存活的线程名字: {[t.name for t in threading.enumerate()]}')


# """停止串口通信线程"""
def stop_serial_threads():
    """停止串口通信线程"""
    global running, serial_port

    running = False

    # 等待所有线程执行完毕
    if receive_thread and receive_thread.is_alive():
        receive_thread.join()
    # if heartbeat_thread and heartbeat_thread.is_alive():
    #     heartbeat_thread.join()  # 暂时取消心跳任务

    # 关闭串口
    if serial_port and serial_port.is_open:
        serial_port.close()
        print("串口已关闭")


# Flask路由

# 主页面路由 - 渲染串口调试工具主界面
@us.route('/usart/set')
def index():
    """主页面"""
    available_ports = find_serial_ports()
    # 使用了 条件表达式 （三元运算符）来检查串口连接状态
    is_connected = serial_port and serial_port.is_open if serial_port else False

    return render_template('index.html',
                           ports=available_ports,  # 串口列表
                           is_connected=is_connected,  # 连接状态
                           port_name=port_name,  # 端口名称
                           baud_rate=baud_rate)  # 波特率


# 获取可用串口列表API - 返回系统检测到的所有可用串口设备
@us.route('/api/ports')
def get_ports():
    """获取可用串口列表"""
    ports = find_serial_ports()
    return jsonify(ports)


# 串口连接API - 根据前端选择的串口和波特率建立串口连接
@us.route('/api/connect', methods=['POST'])
def connect_serial():
    """连接串口"""
    data = request.get_json()
    port = data.get('port')
    baud = data.get('baud', 9600)

    if not port:
        return jsonify({'success': False, 'message': '请选择串口'})

    # 先停止现有线程
    stop_serial_threads()

    # 初始化串口
    success = serial_init(port, baud)

    if success:
        # 启动线程
        start_serial_threads()
        return jsonify({'success': True, 'message': f'串口 {port} 连接成功'})
    else:
        return jsonify({'success': False, 'message': f'串口 {port} 连接失败'})


# 串口断开连接API - 关闭当前串口连接并停止相关线程
@us.route('/api/disconnect', methods=['POST'])
def disconnect_serial():
    """断开串口连接"""
    stop_serial_threads()
    return jsonify({'success': True, 'message': '串口已断开'})


# 数据发送API - 根据数据格式（文本/16进制）验证并发送数据到串口
@us.route('/api/send', methods=['POST'])
def send_data():
    """发送数据到串口 - 后端统一处理数据验证和发送"""
    data = request.get_json()
    message = data.get('message', '')
    format_type = data.get('format', 'text')  # text 或 hex

    if not message:
        return jsonify({'success': False, 'message': '请输入要发送的数据'})

    # 根据数据格式进行验证和处理
    send_message = message
    
    if format_type == 'hex':
        # 16进制模式：移除空格，验证是否为有效16进制
        hex_string = message.replace(' ', '')
        if not re.match(r'^[0-9A-Fa-f]+$', hex_string):
            return jsonify({'success': False, 'message': '请输入有效的16进制数据（只能包含0-9, A-F, a-f）'})
        if len(hex_string) % 2 != 0:
            return jsonify({'success': False, 'message': '16进制数据长度必须为偶数'})
        send_message = hex_string
        success = serial_send(hex_string, 'hex')
    else:
        # 文本模式：直接发送
        success = serial_send(message + '\n', 'text')

    if success:
        with thread_lock:
            received_data.append({
                'type': 'sent',
                'data': message,
                'time': time.strftime('%H:%M:%S'),
                'format': format_type
            })
        return jsonify({
            'success': True, 
            'message': f'数据发送成功（{format_type}格式）',
            'format': format_type,
            'data': send_message
        })
    else:
        return jsonify({'success': False, 'message': '数据发送失败，请检查串口连接'})


# 连接状态查询API - 返回当前串口连接状态、端口名称和波特率
@us.route('/api/status')
def get_status():
    """获取连接状态"""
    is_connected = serial_port and serial_port.is_open if serial_port else False

    return jsonify({
        'connected': is_connected,
        'port_name': port_name,
        'baud_rate': baud_rate
    })


# 接收数据查询API - 返回串口接收到的所有数据记录
@us.route('/api/data')
def get_received_data():
    """获取接收到的数据"""
    with thread_lock:
        data = received_data.copy()
    return jsonify(data)


# 清空数据API - 清除所有已接收的串口数据记录
@us.route('/api/clear', methods=['POST'])
def clear_data():
    """清空接收数据"""
    with thread_lock:
        received_data.clear()
    return jsonify({'success': True, 'message': '数据已清空'})


# 心跳包配置API - 启用/禁用心跳包功能或设置心跳发送间隔
@us.route('/api/heartbeat', methods=['POST'])
def configure_heartbeat():
    """配置心跳包"""
    global heartbeat_enabled, heartbeat_interval

    data = request.get_json()

    if 'enabled' in data:
        heartbeat_enabled = data['enabled']
        status = '启用' if heartbeat_enabled else '禁用'
        return jsonify({'success': True, 'message': f'心跳包已{status}'})

    if 'interval' in data:
        interval = data['interval']
        if interval in [5, 10, 30, 60]:
            heartbeat_interval = interval
            return jsonify({'success': True, 'message': f'心跳间隔已设置为 {interval} 秒'})
        else:
            return jsonify({'success': False, 'message': '无效的心跳间隔'})

    return jsonify({'success': False, 'message': '无效的配置参数'})


# CRC8校验计算API - 根据输入的16进制数据计算CRC8校验值
@us.route('/api/crc8', methods=['POST'])
def calculate_crc8_api():
    """计算CRC8校验值API"""
    data = request.get_json()
    
    if 'data' not in data:
        return jsonify({'success': False, 'message': '缺少数据参数'})
    
    try:
        # 将前端传入的16进制字符串转换为字节数组
        hex_data = data['data']
        if isinstance(hex_data, str):
            # 移除空格，转换为字节数组
            hex_string = hex_data.replace(' ', '')
            if not re.match(r'^[0-9A-Fa-f]+$', hex_string):
                return jsonify({'success': False, 'message': '无效的16进制数据'})
            if len(hex_string) % 2 != 0:
                return jsonify({'success': False, 'message': '16进制数据长度必须为偶数'})
            
            # 将16进制字符串转换为字节数组
            byte_array = []
            for i in range(0, len(hex_string), 2):
                byte_array.append(int(hex_string[i:i+2], 16))
        else:
            # 如果传入的是数组，直接使用
            byte_array = hex_data
        
        # 计算CRC8
        crc_result = calculate_crc8(byte_array)
        
        return jsonify({
            'success': True, 
            'crc': f'{crc_result:02X}',
            'message': 'CRC8计算成功'
        })
        
    except Exception as e:
        return jsonify({'success': False, 'message': f'CRC8计算失败: {str(e)}'})

# 灯光控制API - 根据开关状态生成并发送灯光控制数据帧
@us.route('/api/light-control', methods=['POST'])
def light_control_api():
    """灯光控制API - 后端处理数据帧整理和发送"""
    data = request.get_json()
    
    if 'isOn' not in data:
        return jsonify({'success': False, 'message': '缺少控制参数'})
    
    # 检查串口连接状态
    if not (serial_port and serial_port.is_open):
        return jsonify({'success': False, 'message': '串口未连接'})
    
    try:
        is_on = data['isOn']
        
        # 帧格式: 55 03 01 [数据] [CRC]
        frame_header = 0x55
        frame_id = 0x03
        data_length = 0x01
        data_value = 0x01 if is_on else 0x00
        
        # 构建数据帧（不含CRC）
        frame_data = [frame_header, frame_id, data_length, data_value]
        
        # 计算CRC8校验
        crc = calculate_crc8(frame_data)
        
        # 完整帧数据
        full_frame = frame_data + [crc]
        
        # 转换为HEX字符串
        hex_string = ''.join([f'{b:02X}' for b in full_frame])
        
        # 发送数据到串口
        success = serial_send(hex_string, 'hex')
        
        if success:
            # 记录发送数据
            with thread_lock:
                received_data.append({
                    'type': 'sent',
                    'data': hex_string,
                    'time': time.strftime('%H:%M:%S'),
                    'format': 'hex'
                })
            
            action = '开' if is_on else '关'
            return jsonify({
                'success': True, 
                'message': f'灯光已{action}',
                'data': hex_string
            })
        else:
            return jsonify({'success': False, 'message': '数据发送失败'})
            
    except Exception as e:
        return jsonify({'success': False, 'message': f'灯光控制失败: {str(e)}'})


# 设备状态查询API - 检测接收数据中的特定帧以判断设备状态（灯光开/关）
@us.route('/api/device-status', methods=['GET'])
def get_device_status():
    """获取设备状态 - 后端统一处理数据解析"""
    try:
        # 默认状态为未知
        device_status = 'unknown'
        
        # 获取最新的接收数据
        with thread_lock:
            if received_data:
                # 查找最新的接收数据（类型为'received'且格式为'hex'）
                for item in reversed(received_data):
                    if item['type'] == 'received' and item['format'] == 'hex':
                        # 移除空格并转换为大写进行比较
                        clean_data = item['data'].replace(' ', '').upper()
                        
                        # 检测 55 02 01 01 76 (灯光开)
                        if clean_data == '5502010176':
                            device_status = 'light_on'
                            break
                        # 检测 55 02 01 00 71 (灯光关)
                        elif clean_data == '5502010071':
                            device_status = 'light_off'
                            break
        
        return jsonify({
            'success': True,
            'status': device_status,
            'message': '设备状态获取成功'
        })
        
    except Exception as e:
        return jsonify({
            'success': False,
            'status': 'unknown',
            'message': f'设备状态获取失败: {str(e)}'
        })
